<?php
/**
 * CActiveForm class file.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.yiiframework.com/
 * @copyright 2008-2013 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

/**
 * CActiveForm provides a set of methods that can help to simplify the creation
 * of complex and interactive HTML forms that are associated with data models.
 *
 * The 'beginWidget' and 'endWidget' call of CActiveForm widget will render
 * the open and close form tags. Most other methods of CActiveForm are wrappers
 * of the corresponding 'active' methods in {@link CHtml}. Calling them in between
 * the 'beginWidget' and 'endWidget' calls will render text labels, input fields,
 * etc. For example, calling {@link CActiveForm::textField}
 * would generate an input field for a specified model attribute.
 *
 * What makes CActiveForm extremely useful is its support for data validation.
 * CActiveForm supports data validation at three levels:
 * <ul>
 * <li>server-side validation: the validation is performed at server side after
 * the whole page containing the form is submitted. If there is any validation error,
 * CActiveForm will render the error in the page back to user.</li>
 * <li>AJAX-based validation: when the user enters data into an input field,
 * an AJAX request is triggered which requires server-side validation. The validation
 * result is sent back in AJAX response and the input field changes its appearance
 * accordingly.</li>
 * <li>client-side validation (available since version 1.1.7):
 * when the user enters data into an input field,
 * validation is performed on the client side using JavaScript. No server contact
 * will be made, which reduces the workload on the server.</li>
 * </ul>
 *
 * All these validations share the same set of validation rules declared in
 * the associated model class. CActiveForm is designed in such a way that
 * all these validations will lead to the same user interface changes and error
 * message content.
 *
 * To ensure data validity, server-side validation is always performed.
 * By setting {@link enableAjaxValidation} to true, one can enable AJAX-based validation;
 * and by setting {@link enableClientValidation} to true, one can enable client-side validation.
 * Note that in order to make the latter two validations work, the user's browser
 * must has its JavaScript enabled. If not, only the server-side validation will
 * be performed.
 *
 * The AJAX-based validation and client-side validation may be used together
 * or separately. For example, in a user registration form, one may use AJAX-based
 * validation to check if the user has picked a unique username, and use client-side
 * validation to ensure all required fields are entered with data.
 * Because the AJAX-based validation may bring extra workload on the server,
 * if possible, one should mainly use client-side validation.
 *
 * The AJAX-based validation has a few limitations. First, it does not work
 * with file upload fields. Second, it should not be used to perform validations that
 * may cause server-side state changes. Third, it is not designed
 * to work with tabular data input for the moment.
 *
 * Support for client-side validation varies for different validators. A validator
 * will support client-side validation only if it implements {@link CValidator::clientValidateAttribute}
 * and has its {@link CValidator::enableClientValidation} property set true.
 * At this moment, the following core validators support client-side validation:
 * <ul>
 * <li>{@link CBooleanValidator}</li>
 * <li>{@link CCaptchaValidator}</li>
 * <li>{@link CCompareValidator}</li>
 * <li>{@link CEmailValidator}</li>
 * <li>{@link CNumberValidator}</li>
 * <li>{@link CRangeValidator}</li>
 * <li>{@link CRegularExpressionValidator}</li>
 * <li>{@link CRequiredValidator}</li>
 * <li>{@link CStringValidator}</li>
 * <li>{@link CUrlValidator}</li>
 * </ul>
 *
 * CActiveForm relies on CSS to customize the appearance of input fields
 * which are in different validation states. In particular, each input field
 * may be one of the four states: initial (not validated),
 * validating, error and success. To differentiate these states, CActiveForm
 * automatically assigns different CSS classes for the last three states
 * to the HTML element containing the input field.
 * By default, these CSS classes are named as 'validating', 'error' and 'success',
 * respectively. We may customize these CSS classes by configuring the
 * {@link clientOptions} property or specifying in the {@link error} method.
 *
 * The following is a piece of sample view code showing how to use CActiveForm:
 *
 * <pre>
 * <?php $form = $this->beginWidget('CActiveForm', array(
 *     'id'=>'user-form',
 *     'enableAjaxValidation'=>true,
 *     'enableClientValidation'=>true,
 *     'focus'=>array($model,'firstName'),
 * )); ?>
 *
 * <?php echo $form->errorSummary($model); ?>
 *
 * <div class="row">
 *     <?php echo $form->labelEx($model,'firstName'); ?>
 *     <?php echo $form->textField($model,'firstName'); ?>
 *     <?php echo $form->error($model,'firstName'); ?>
 * </div>
 * <div class="row">
 *     <?php echo $form->labelEx($model,'lastName'); ?>
 *     <?php echo $form->textField($model,'lastName'); ?>
 *     <?php echo $form->error($model,'lastName'); ?>
 * </div>
 *
 * <?php $this->endWidget(); ?>
 * </pre>
 *
 * To respond to the AJAX validation requests, we need the following class code:
 * <pre>
 * public function actionCreate()
 * {
 *     $model=new User;
 *     $this->performAjaxValidation($model);
 *     if(isset($_POST['User']))
 *     {
 *         $model->attributes=$_POST['User'];
 *         if($model->save())
 *             $this->redirect('index');
 *     }
 *     $this->render('create',array('model'=>$model));
 * }
 *
 * protected function performAjaxValidation($model)
 * {
 *     if(isset($_POST['ajax']) && $_POST['ajax']==='user-form')
 *     {
 *         echo CActiveForm::validate($model);
 *         Yii::app()->end();
 *     }
 * }
 * </pre>
 *
 * In the above code, if we do not enable the AJAX-based validation, we can remove
 * the <code>performAjaxValidation</code> method and its invocation.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @package system.web.widgets
 * @since 1.1.1
 */
class CActiveForm extends CWidget
{
    /**
     * @var mixed the form action URL (see {@link CHtml::normalizeUrl} for details about this parameter).
     * If not set, the current page URL is used.
     */
    public $action = '';
    /**
     * @var string the form submission method. This should be either 'post' or 'get'.
     * Defaults to 'post'.
     */
    public $method = 'post';
    /**
     * @var boolean whether to generate a stateful form (See {@link CHtml::statefulForm}). Defaults to false.
     */
    public $stateful = false;
    /**
     * @var string the CSS class name for error messages.
     * Since 1.1.14 this defaults to 'errorMessage' defined in {@link CHtml::$errorMessageCss}.
     * Individual {@link error} call may override this value by specifying the 'class' HTML option.
     */
    public $errorMessageCssClass;
    /**
     * @var array additional HTML attributes that should be rendered for the form tag.
     */
    public $htmlOptions = array();
    /**
     * @var array the options to be passed to the javascript validation plugin.
     * The following options are supported:
     * <ul>
     * <li>ajaxVar: string, the name of the parameter indicating the request is an AJAX request.
     * When the AJAX validation is triggered, a parameter named as this property will be sent
     * together with the other form data to the server. The parameter value is the form ID.
     * The server side can then detect who triggers the AJAX validation and react accordingly.
     * Defaults to 'ajax'.</li>
     * <li>validationUrl: string, the URL that performs the AJAX validations.
     * If not set, it will take the value of {@link action}.</li>
     * <li>validationDelay: integer, the number of milliseconds that an AJAX validation should be
     * delayed after an input is changed. A value 0 means the validation will be triggered immediately
     * when an input is changed. A value greater than 0 means changing several inputs may only
     * trigger a single validation if they happen fast enough, which may help reduce the server load.
     * Defaults to 200 (0.2 second).</li>
     * <li>validateOnSubmit: boolean, whether to perform AJAX validation when the form is being submitted.
     * If there are any validation errors, the form submission will be stopped.
     * Defaults to false.</li>
     * <li>validateOnChange: boolean, whether to trigger an AJAX validation
     * each time when an input's value is changed.    You may want to turn this off
     * if it causes too much performance impact, because each AJAX validation request
     * will submit the data of the whole form. Defaults to true.</li>
     * <li>validateOnType: boolean, whether to trigger an AJAX validation each time when the user
     * presses a key. When setting this property to be true, you should tune up the 'validationDelay'
     * option to avoid triggering too many AJAX validations. Defaults to false.</li>
     * <li>hideErrorMessage: boolean, whether to hide the error message even if there is an error.
     * Defaults to false, which means the error message will show up whenever the input has an error.</li>
     * <li>inputContainer: string, the jQuery selector for the HTML element containing the input field.
     * During the validation process, CActiveForm will set different CSS class for the container element
     * to indicate the state change. If not set, it means the closest 'div' element that contains the input field.</li>
     * <li>errorCssClass: string, the CSS class to be assigned to the container whose associated input
     * has AJAX validation error. Defaults to 'error'.</li>
     * <li>successCssClass: string, the CSS class to be assigned to the container whose associated input
     * passes AJAX validation without any error. Defaults to 'success'.</li>
     * <li>validatingCssClass: string, the CSS class to be assigned to the container whose associated input
     * is currently being validated via AJAX. Defaults to 'validating'.</li>
     * <li>errorMessageCssClass: string, the CSS class assigned to the error messages returned
     * by AJAX validations. Defaults to 'errorMessage'.</li>
     * <li>beforeValidate: function, the function that will be invoked before performing ajax-based validation
     * triggered by form submission action (available only when validateOnSubmit is set true).
     * The expected function signature should be <code>beforeValidate(form) {...}</code>, where 'form' is
     * the jquery representation of the form object. If the return value of this function is NOT true, the validation
     * will be cancelled.
     *
     * Note that because this option refers to a js function, you should wrap the value with {@link CJavaScriptExpression} to prevent it
     * from being encoded as a string. This option has been available since version 1.1.3.</li>
     * <li>afterValidate: function, the function that will be invoked after performing ajax-based validation
     * triggered by form submission action (available only when validateOnSubmit is set true).
     * The expected function signature should be <code>afterValidate(form, data, hasError) {...}</code>, where 'form' is
     * the jquery representation of the form object; 'data' is the JSON response from the server-side validation; 'hasError'
     * is a boolean value indicating whether there is any validation error. If the return value of this function is NOT true,
     * the normal form submission will be cancelled.
     *
     * Note that because this option refers to a js function, you should wrap the value with {@link CJavaScriptExpression} to prevent it
     * from being encoded as a string. This option has been available since version 1.1.3.</li>
     * <li>beforeValidateAttribute: function, the function that will be invoked before performing ajax-based validation
     * triggered by a single attribute input change. The expected function signature should be
     * <code>beforeValidateAttribute(form, attribute) {...}</code>, where 'form' is the jquery representation of the form object
     * and 'attribute' refers to the js options for the triggering attribute (see {@link error}).
     * If the return value of this function is NOT true, the validation will be cancelled.
     *
     * Note that because this option refers to a js function, you should wrap the value with {@link CJavaScriptExpression} to prevent it
     * from being encoded as a string. This option has been available since version 1.1.3.</li>
     * <li>afterValidateAttribute: function, the function that will be invoked after performing ajax-based validation
     * triggered by a single attribute input change. The expected function signature should be
     * <code>afterValidateAttribute(form, attribute, data, hasError) {...}</code>, where 'form' is the jquery
     * representation of the form object; 'attribute' refers to the js options for the triggering attribute (see {@link error});
     * 'data' is the JSON response from the server-side validation; 'hasError' is a boolean value indicating whether
     * there is any validation error.
     *
     * Note that because this option refers to a js function, you should wrap the value with {@link CJavaScriptExpression} to prevent it
     * from being encoded as a string. This option has been available since version 1.1.3.</li>
     * </ul>
     *
     * Some of the above options may be overridden in individual calls of {@link error()}.
     * They include: validationDelay, validateOnChange, validateOnType, hideErrorMessage,
     * inputContainer, errorCssClass, successCssClass, validatingCssClass, beforeValidateAttribute, afterValidateAttribute.
     */
    public $clientOptions = array();
    /**
     * @var boolean whether to enable data validation via AJAX. Defaults to false.
     * When this property is set true, you should respond to the AJAX validation request on the server side as shown below:
     * <pre>
     * public function actionCreate()
     * {
     *     $model=new User;
     *     if(isset($_POST['ajax']) && $_POST['ajax']==='user-form')
     *     {
     *         echo CActiveForm::validate($model);
     *         Yii::app()->end();
     *     }
     *     ......
     * }
     * </pre>
     */
    public $enableAjaxValidation = false;
    /**
     * @var boolean whether to enable client-side data validation. Defaults to false.
     *
     * When this property is set true, client-side validation will be performed by validators
     * that support it (see {@link CValidator::enableClientValidation} and {@link CValidator::clientValidateAttribute}).
     *
     * @see error
     * @since 1.1.7
     */
    public $enableClientValidation = false;

    /**
     * @var mixed form element to get initial input focus on page load.
     *
     * Defaults to null meaning no input field has a focus.
     * If set as array, first element should be model and second element should be the attribute.
     * If set as string any jQuery selector can be used
     *
     * Example - set input focus on page load to:
     * <ul>
     * <li>'focus'=>array($model,'username') - $model->username input filed</li>
     * <li>'focus'=>'#'.CHtml::activeId($model,'username') - $model->username input field</li>
     * <li>'focus'=>'#LoginForm_username' - input field with ID LoginForm_username</li>
     * <li>'focus'=>'input[type="text"]:first' - first input element of type text</li>
     * <li>'focus'=>'input:visible:enabled:first' - first visible and enabled input element</li>
     * <li>'focus'=>'input:text[value=""]:first' - first empty input</li>
     * </ul>
     *
     * @since 1.1.4
     */
    public $focus;
    /**
     * @var array the javascript options for model attributes (input ID => options)
     * @see error
     * @since 1.1.7
     */
    protected $attributes = array();
    /**
     * @var string the ID of the container element for error summary
     * @see errorSummary
     * @since 1.1.7
     */
    protected $summaryID;
    /**
     * @var string[] attribute IDs to be used to display error summary.
     * @since 1.1.14
     */
    private $_summaryAttributes = array();

    /**
     * Initializes the widget.
     * This renders the form open tag.
     */
    public function init()
    {
        if (!isset($this->htmlOptions['id']))
            $this->htmlOptions['id'] = $this->id;
        else
            $this->id = $this->htmlOptions['id'];

        if ($this->stateful)
            echo CHtml::statefulForm($this->action, $this->method, $this->htmlOptions);
        else
            echo CHtml::beginForm($this->action, $this->method, $this->htmlOptions);

        if ($this->errorMessageCssClass === null)
            $this->errorMessageCssClass = CHtml::$errorMessageCss;
    }

    /**
     * Runs the widget.
     * This registers the necessary javascript code and renders the form close tag.
     */
    public function run()
    {
        if (is_array($this->focus))
            $this->focus = "#" . CHtml::activeId($this->focus[0], $this->focus[1]);

        echo CHtml::endForm();
        $cs = Yii::app()->clientScript;
        if (!$this->enableAjaxValidation && !$this->enableClientValidation || empty($this->attributes)) {
            if ($this->focus !== null) {
                $cs->registerCoreScript('jquery');
                $cs->registerScript('CActiveForm#focus', "
					if(!window.location.hash)
						jQuery('" . $this->focus . "').focus();
				");
            }
            return;
        }

        $options = $this->clientOptions;
        if (isset($this->clientOptions['validationUrl']) && is_array($this->clientOptions['validationUrl']))
            $options['validationUrl'] = CHtml::normalizeUrl($this->clientOptions['validationUrl']);

        foreach ($this->_summaryAttributes as $attribute)
            $this->attributes[$attribute]['summary'] = true;
        $options['attributes'] = array_values($this->attributes);

        if ($this->summaryID !== null)
            $options['summaryID'] = $this->summaryID;

        if ($this->focus !== null)
            $options['focus'] = $this->focus;

        if (!empty(CHtml::$errorCss))
            $options['errorCss'] = CHtml::$errorCss;

        $options = CJavaScript::encode($options);
        $cs->registerCoreScript('yiiactiveform');
        $id = $this->id;
        $cs->registerScript(__CLASS__ . '#' . $id, "jQuery('#$id').yiiactiveform($options);");
    }

    /**
     * Displays the first validation error for a model attribute.
     * This is similar to {@link CHtml::error} except that it registers the model attribute
     * so that if its value is changed by users, an AJAX validation may be triggered.
     * @param CModel $model the data model
     * @param string $attribute the attribute name
     * @param array $htmlOptions additional HTML attributes to be rendered in the container div tag.
     * Besides all those options available in {@link CHtml::error}, the following options are recognized in addition:
     * <ul>
     * <li>validationDelay</li>
     * <li>validateOnChange</li>
     * <li>validateOnType</li>
     * <li>hideErrorMessage</li>
     * <li>inputContainer</li>
     * <li>errorCssClass</li>
     * <li>successCssClass</li>
     * <li>validatingCssClass</li>
     * <li>beforeValidateAttribute</li>
     * <li>afterValidateAttribute</li>
     * </ul>
     * These options override the corresponding options as declared in {@link options} for this
     * particular model attribute. For more details about these options, please refer to {@link clientOptions}.
     * Note that these options are only used when {@link enableAjaxValidation} or {@link enableClientValidation}
     * is set true.
     * <ul>
     * <li>inputID</li>
     * </ul>
     * When an CActiveForm input field uses a custom ID, for ajax/client validation to work properly
     * inputID should be set to the same ID
     *
     * Example:
     * <pre>
     * <div class="form-element">
     *    <?php echo $form->labelEx($model,'attribute'); ?>
     *    <?php echo $form->textField($model,'attribute', array('id'=>'custom-id')); ?>
     *    <?php echo $form->error($model,'attribute',array('inputID'=>'custom-id')); ?>
     * </div>
     * </pre>
     *
     * When client-side validation is enabled, an option named "clientValidation" is also recognized.
     * This option should take a piece of JavaScript code to perform client-side validation. In the code,
     * the variables are predefined:
     * <ul>
     * <li>value: the current input value associated with this attribute.</li>
     * <li>messages: an array that may be appended with new error messages for the attribute.</li>
     * <li>attribute: a data structure keeping all client-side options for the attribute</li>
     * </ul>
     * This should NOT be a function but just the code, Yii will enclose the code you provide inside the
     * actual JS function.
     * @param boolean $enableAjaxValidation whether to enable AJAX validation for the specified attribute.
     * Note that in order to enable AJAX validation, both {@link enableAjaxValidation} and this parameter
     * must be true.
     * @param boolean $enableClientValidation whether to enable client-side validation for the specified attribute.
     * Note that in order to enable client-side validation, both {@link enableClientValidation} and this parameter
     * must be true. This parameter has been available since version 1.1.7.
     * @return string the validation result (error display or success message).
     * @see CHtml::error
     */
    public function error($model, $attribute, $htmlOptions = array(), $enableAjaxValidation = true, $enableClientValidation = true)
    {
        if (!$this->enableAjaxValidation)
            $enableAjaxValidation = false;
        if (!$this->enableClientValidation)
            $enableClientValidation = false;

        if (!isset($htmlOptions['class']))
            $htmlOptions['class'] = $this->errorMessageCssClass;

        if (!$enableAjaxValidation && !$enableClientValidation)
            return CHtml::error($model, $attribute, $htmlOptions);

        $id = CHtml::activeId($model, $attribute);
        $inputID = isset($htmlOptions['inputID']) ? $htmlOptions['inputID'] : $id;
        unset($htmlOptions['inputID']);
        if (!isset($htmlOptions['id']))
            $htmlOptions['id'] = $inputID . '_em_';

        $option = array(
            'id' => $id,
            'inputID' => $inputID,
            'errorID' => $htmlOptions['id'],
            'model' => get_class($model),
            'name' => $attribute,
            'enableAjaxValidation' => $enableAjaxValidation,
        );

        $optionNames = array(
            'validationDelay',
            'validateOnChange',
            'validateOnType',
            'hideErrorMessage',
            'inputContainer',
            'errorCssClass',
            'successCssClass',
            'validatingCssClass',
            'beforeValidateAttribute',
            'afterValidateAttribute',
        );
        foreach ($optionNames as $name) {
            if (isset($htmlOptions[$name])) {
                $option[$name] = $htmlOptions[$name];
                unset($htmlOptions[$name]);
            }
        }
        if ($model instanceof CActiveRecord && !$model->isNewRecord)
            $option['status'] = 1;

        if ($enableClientValidation) {
            $validators = isset($htmlOptions['clientValidation']) ? array($htmlOptions['clientValidation']) : array();
            unset($htmlOptions['clientValidation']);

            $attributeName = $attribute;
            if (($pos = strrpos($attribute, ']')) !== false && $pos !== strlen($attribute) - 1) // e.g. [a]name
            {
                $attributeName = substr($attribute, $pos + 1);
            }

            foreach ($model->getValidators($attributeName) as $validator) {
                if ($validator->enableClientValidation) {
                    if (($js = $validator->clientValidateAttribute($model, $attributeName)) != '')
                        $validators[] = $js;
                }
            }
            if ($validators !== array())
                $option['clientValidation'] = new CJavaScriptExpression("function(value, messages, attribute) {\n" . implode("\n", $validators) . "\n}");
        }

        $html = CHtml::error($model, $attribute, $htmlOptions);
        if ($html === '') {
            if (isset($htmlOptions['style']))
                $htmlOptions['style'] = rtrim($htmlOptions['style'], ';') . ';display:none';
            else
                $htmlOptions['style'] = 'display:none';
            $html = CHtml::tag(CHtml::$errorContainerTag, $htmlOptions, '');
        }

        $this->attributes[$inputID] = $option;
        return $html;
    }

    /**
     * Displays a summary of validation errors for one or several models.
     * This method is very similar to {@link CHtml::errorSummary} except that it also works
     * when AJAX validation is performed.
     * @param mixed $models the models whose input errors are to be displayed. This can be either
     * a single model or an array of models.
     * @param string $header a piece of HTML code that appears in front of the errors
     * @param string $footer a piece of HTML code that appears at the end of the errors
     * @param array $htmlOptions additional HTML attributes to be rendered in the container div tag.
     * @return string the error summary. Empty if no errors are found.
     * @see CHtml::errorSummary
     */
    public function errorSummary($models, $header = null, $footer = null, $htmlOptions = array())
    {
        if (!$this->enableAjaxValidation && !$this->enableClientValidation)
            return CHtml::errorSummary($models, $header, $footer, $htmlOptions);

        if (!isset($htmlOptions['id']))
            $htmlOptions['id'] = $this->id . '_es_';
        $html = CHtml::errorSummary($models, $header, $footer, $htmlOptions);
        if ($html === '') {
            if ($header === null)
                $header = '<p>' . Yii::t('yii', 'Please fix the following input errors:') . '</p>';
            if (!isset($htmlOptions['class']))
                $htmlOptions['class'] = CHtml::$errorSummaryCss;
            $htmlOptions['style'] = isset($htmlOptions['style']) ? rtrim($htmlOptions['style'], ';') . ';display:none' : 'display:none';
            $html = CHtml::tag('div', $htmlOptions, $header . "\n<ul><li>dummy</li></ul>" . $footer);
        }

        $this->summaryID = $htmlOptions['id'];
        foreach (is_array($models) ? $models : array($models) as $model)
            foreach ($model->getSafeAttributeNames() as $attribute)
                $this->_summaryAttributes[] = CHtml::activeId($model, $attribute);

        return $html;
    }

    /**
     * Renders an HTML label for a model attribute.
     * This method is a wrapper of {@link CHtml::activeLabel}.
     * Please check {@link CHtml::activeLabel} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated label tag
     */
    public function label($model, $attribute, $htmlOptions = array())
    {
        return CHtml::activeLabel($model, $attribute, $htmlOptions);
    }

    /**
     * Renders an HTML label for a model attribute.
     * This method is a wrapper of {@link CHtml::activeLabelEx}.
     * Please check {@link CHtml::activeLabelEx} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated label tag
     */
    public function labelEx($model, $attribute, $htmlOptions = array())
    {
        return CHtml::activeLabelEx($model, $attribute, $htmlOptions);
    }

    /**
     * Renders a url field for a model attribute.
     * This method is a wrapper of {@link CHtml::activeUrlField}.
     * Please check {@link CHtml::activeUrlField} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated input field
     * @since 1.1.11
     */
    public function urlField($model, $attribute, $htmlOptions = array())
    {
        return CHtml::activeUrlField($model, $attribute, $htmlOptions);
    }

    /**
     * Renders an email field for a model attribute.
     * This method is a wrapper of {@link CHtml::activeEmailField}.
     * Please check {@link CHtml::activeEmailField} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated input field
     * @since 1.1.11
     */
    public function emailField($model, $attribute, $htmlOptions = array())
    {
        return CHtml::activeEmailField($model, $attribute, $htmlOptions);
    }

    /**
     * Renders a number field for a model attribute.
     * This method is a wrapper of {@link CHtml::activeNumberField}.
     * Please check {@link CHtml::activeNumberField} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated input field
     * @since 1.1.11
     */
    public function numberField($model, $attribute, $htmlOptions = array())
    {
        return CHtml::activeNumberField($model, $attribute, $htmlOptions);
    }

    /**
     * Generates a range field for a model attribute.
     * This method is a wrapper of {@link CHtml::activeRangeField}.
     * Please check {@link CHtml::activeRangeField} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated input field
     * @since 1.1.11
     */
    public function rangeField($model, $attribute, $htmlOptions = array())
    {
        return CHtml::activeRangeField($model, $attribute, $htmlOptions);
    }

    /**
     * Renders a date field for a model attribute.
     * This method is a wrapper of {@link CHtml::activeDateField}.
     * Please check {@link CHtml::activeDateField} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated input field
     * @since 1.1.11
     */
    public function dateField($model, $attribute, $htmlOptions = array())
    {
        return CHtml::activeDateField($model, $attribute, $htmlOptions);
    }


    /**
     * Renders a time field for a model attribute.
     * This method is a wrapper of {@link CHtml::activeTimeField}.
     * Please check {@link CHtml::activeTimeField} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated input field
     * @since 1.1.14
     */
    public function timeField($model, $attribute, $htmlOptions = array())
    {
        return CHtml::activeTimeField($model, $attribute, $htmlOptions);
    }

    /**
     * Renders a time field for a model attribute.
     * This method is a wrapper of {@link CHtml::activeTimeField}.
     * Please check {@link CHtml::activeTimeField} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated input field
     * @since 1.1.14
     */
    public function telField($model, $attribute, $htmlOptions = array())
    {
        return CHtml::activeTelField($model, $attribute, $htmlOptions);
    }

    /**
     * Renders a text field for a model attribute.
     * This method is a wrapper of {@link CHtml::activeTextField}.
     * Please check {@link CHtml::activeTextField} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated input field
     */
    public function textField($model, $attribute, $htmlOptions = array())
    {
        return CHtml::activeTextField($model, $attribute, $htmlOptions);
    }

    /**
     * Renders a search field for a model attribute.
     * This method is a wrapper of {@link CHtml::activeSearchField}.
     * Please check {@link CHtml::activeSearchField} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated input field
     * @since 1.1.14
     */
    public function searchField($model, $attribute, $htmlOptions = array())
    {
        return CHtml::activeSearchField($model, $attribute, $htmlOptions);
    }

    /**
     * Renders a hidden field for a model attribute.
     * This method is a wrapper of {@link CHtml::activeHiddenField}.
     * Please check {@link CHtml::activeHiddenField} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated input field
     */
    public function hiddenField($model, $attribute, $htmlOptions = array())
    {
        return CHtml::activeHiddenField($model, $attribute, $htmlOptions);
    }

    /**
     * Renders a password field for a model attribute.
     * This method is a wrapper of {@link CHtml::activePasswordField}.
     * Please check {@link CHtml::activePasswordField} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated input field
     */
    public function passwordField($model, $attribute, $htmlOptions = array())
    {
        return CHtml::activePasswordField($model, $attribute, $htmlOptions);
    }

    /**
     * Renders a text area for a model attribute.
     * This method is a wrapper of {@link CHtml::activeTextArea}.
     * Please check {@link CHtml::activeTextArea} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated text area
     */
    public function textArea($model, $attribute, $htmlOptions = array())
    {
        return CHtml::activeTextArea($model, $attribute, $htmlOptions);
    }

    /**
     * Renders a file field for a model attribute.
     * This method is a wrapper of {@link CHtml::activeFileField}.
     * Please check {@link CHtml::activeFileField} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $htmlOptions additional HTML attributes
     * @return string the generated input field
     */
    public function fileField($model, $attribute, $htmlOptions = array())
    {
        return CHtml::activeFileField($model, $attribute, $htmlOptions);
    }

    /**
     * Renders a radio button for a model attribute.
     * This method is a wrapper of {@link CHtml::activeRadioButton}.
     * Please check {@link CHtml::activeRadioButton} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated radio button
     */
    public function radioButton($model, $attribute, $htmlOptions = array())
    {
        return CHtml::activeRadioButton($model, $attribute, $htmlOptions);
    }

    /**
     * Renders a checkbox for a model attribute.
     * This method is a wrapper of {@link CHtml::activeCheckBox}.
     * Please check {@link CHtml::activeCheckBox} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated check box
     */
    public function checkBox($model, $attribute, $htmlOptions = array())
    {
        return CHtml::activeCheckBox($model, $attribute, $htmlOptions);
    }

    /**
     * Renders a dropdown list for a model attribute.
     * This method is a wrapper of {@link CHtml::activeDropDownList}.
     * Please check {@link CHtml::activeDropDownList} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $data data for generating the list options (value=>display)
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated drop down list
     */
    public function dropDownList($model, $attribute, $data, $htmlOptions = array())
    {
        return CHtml::activeDropDownList($model, $attribute, $data, $htmlOptions);
    }

    /**
     * Renders a list box for a model attribute.
     * This method is a wrapper of {@link CHtml::activeListBox}.
     * Please check {@link CHtml::activeListBox} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $data data for generating the list options (value=>display)
     * @param array $htmlOptions additional HTML attributes.
     * @return string the generated list box
     */
    public function listBox($model, $attribute, $data, $htmlOptions = array())
    {
        return CHtml::activeListBox($model, $attribute, $data, $htmlOptions);
    }

    /**
     * Renders a checkbox list for a model attribute.
     * This method is a wrapper of {@link CHtml::activeCheckBoxList}.
     * Please check {@link CHtml::activeCheckBoxList} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $data value-label pairs used to generate the check box list.
     * @param array $htmlOptions addtional HTML options.
     * @return string the generated check box list
     */
    public function checkBoxList($model, $attribute, $data, $htmlOptions = array())
    {
        return CHtml::activeCheckBoxList($model, $attribute, $data, $htmlOptions);
    }

    /**
     * Renders a radio button list for a model attribute.
     * This method is a wrapper of {@link CHtml::activeRadioButtonList}.
     * Please check {@link CHtml::activeRadioButtonList} for detailed information
     * about the parameters for this method.
     * @param CModel $model the data model
     * @param string $attribute the attribute
     * @param array $data value-label pairs used to generate the radio button list.
     * @param array $htmlOptions addtional HTML options.
     * @return string the generated radio button list
     */
    public function radioButtonList($model, $attribute, $data, $htmlOptions = array())
    {
        return CHtml::activeRadioButtonList($model, $attribute, $data, $htmlOptions);
    }

    /**
     * Validates one or several models and returns the results in JSON format.
     * This is a helper method that simplifies the way of writing AJAX validation code.
     * @param mixed $models a single model instance or an array of models.
     * @param array $attributes list of attributes that should be validated. Defaults to null,
     * meaning any attribute listed in the applicable validation rules of the models should be
     * validated. If this parameter is given as a list of attributes, only
     * the listed attributes will be validated.
     * @param boolean $loadInput whether to load the data from $_POST array in this method.
     * If this is true, the model will be populated from <code>$_POST[ModelClass]</code>.
     * @return string the JSON representation of the validation error messages.
     */
    public static function validate($models, $attributes = null, $loadInput = true)
    {
        $result = array();
        if (!is_array($models))
            $models = array($models);
        foreach ($models as $model) {
            $modelName = CHtml::modelName($model);
            if ($loadInput && isset($_POST[$modelName]))
                $model->attributes = $_POST[$modelName];
            $model->validate($attributes);
            foreach ($model->getErrors() as $attribute => $errors)
                $result[CHtml::activeId($model, $attribute)] = $errors;
        }
        return function_exists('json_encode') ? json_encode($result) : CJSON::encode($result);
    }

    /**
     * Validates an array of model instances and returns the results in JSON format.
     * This is a helper method that simplifies the way of writing AJAX validation code for tabular input.
     * @param mixed $models an array of model instances.
     * @param array $attributes list of attributes that should be validated. Defaults to null,
     * meaning any attribute listed in the applicable validation rules of the models should be
     * validated. If this parameter is given as a list of attributes, only
     * the listed attributes will be validated.
     * @param boolean $loadInput whether to load the data from $_POST array in this method.
     * If this is true, the model will be populated from <code>$_POST[ModelClass][$i]</code>.
     * @return string the JSON representation of the validation error messages.
     */
    public static function validateTabular($models, $attributes = null, $loadInput = true)
    {
        $result = array();
        if (!is_array($models))
            $models = array($models);
        foreach ($models as $i => $model) {
            $modelName = CHtml::modelName($model);
            if ($loadInput && isset($_POST[$modelName][$i]))
                $model->attributes = $_POST[$modelName][$i];
            $model->validate($attributes);
            foreach ($model->getErrors() as $attribute => $errors)
                $result[CHtml::activeId($model, '[' . $i . ']' . $attribute)] = $errors;
        }
        return function_exists('json_encode') ? json_encode($result) : CJSON::encode($result);
    }
}
